<#
.SYNOPSIS
标准化媒体文件音量并转换格式

.DESCRIPTION
递归处理指定目录中的音频和视频文件：
1. 扫描并分析所有媒体文件的音频流（LUFS和真峰值）
2. 计算全局音量增益参数，统一响度水平
3. 调整音量并转换格式（音频→320k MP3，视频→CRF18 H.264 + 320k AAC）
4. 删除原始非MP3/MP4文件
5. 提供实时进度条和详细处理报告

.PARAMETER Path
要处理的目录路径（默认为当前目录）
支持别名: Directory, FullName, PSPath

.PARAMETER TargetPeak
目标峰值电平（dB），默认为-3dB
取值范围: -24.0 到 -0.1 (推荐值: -3.0 到 -1.0)

.EXAMPLE
Optimize-MediaVolume -Path "D:\Media"
处理D:\Media目录中的所有媒体文件

.EXAMPLE
Optimize-MediaVolume "E:\Music" -TargetPeak -2.5
处理E:\Music目录，目标峰值-2.5dB

.EXAMPLE
Optimize-MediaVolume -WhatIf
模拟运行，显示将要执行的操作但不实际修改文件

.EXAMPLE
Optimize-MediaVolume -Verbose
显示详细处理信息（推荐使用）

.EXAMPLE
"F:\Videos" | Optimize-MediaVolume
通过管道输入处理目录

.NOTES
功能特性:
- 彩色进度显示和实时状态更新
- 支持 -WhatIf 和 -Confirm 安全模式
- 详细日志 (-Verbose) 和调试信息 (-Debug)
- 返回结构化处理结果对象

作者: CatLemon
版本: 2.0
更新日期: 2023-10-15
要求: 
  - PowerShell 5.1+
  - FFmpeg 已安装并添加到系统PATH
  - 执行策略允许运行脚本 (Set-ExecutionPolicy RemoteSigned)
#>
function Optimize-MediaVolume {
    [CmdletBinding(SupportsShouldProcess = $true, 
        ConfirmImpact = 'Medium',
        DefaultParameterSetName = 'Default')]
    [Alias("opmv", "Normalize-Media")]
    [OutputType([System.Management.Automation.PSCustomObject])]
    param(
        [Parameter(Position = 0, 
            Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "Default")]
        [Alias("Directory", "FullName", "PSPath")]
        [ValidateScript({
                if (-not (Test-Path $_ -PathType Container)) {
                    throw "路径不存在或不是目录: $_"
                }
                $true
            })]
        [string]$Path = ".",

        [Parameter(Position = 1,
            Mandatory = $false,
            ParameterSetName = "Default")]
        [ValidateRange(-24, -0.1)]
        [double]$TargetPeak = -3
    )

    begin {
        # 初始化变量
        $baseExtensions = @('.wav', '.m4a', '.mp3', '.flac', '.ogg', '.aac', '.wma', '.ape', '.alac', '.opus',
            '.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv', '.webm', '.mpeg', '.mpg', '.ts')
        $searchPatterns = $baseExtensions | ForEach-Object { "*$_" }
        # $audioExtensions = $baseExtensions[0..9]
        $videoExtensions = $baseExtensions[10..19]
        $analysisData = @()
        $results = @()
    }
    
    process {
        # 第一步：收集所有媒体文件并分析音频流
        $files = Get-ChildItem -LiteralPath $Path -Recurse -Include $searchPatterns
        if (-not $files) {
            Write-Verbose "在目录 $Path 中未找到支持的媒体文件"
            return
        }

        $analysisFiles = Get-ChildItem ...
        $totalFiles = $analysisFiles.Count
        for ($i = 0; $i -lt $totalFiles; $i++) {
            $file = $analysisFiles[$i]
            Write-Progress -Activity "分析音量" -Status "正在分析: $($file.Name)" -PercentComplete (($i / $totalFiles) * 100)

            $filePath = $file.FullName
            $extension = $file.Extension.ToLower()
            
            # 检测文件是否包含音频流
            $hasAudio = $true
            $ffprobeOutput = ffprobe -v error -show_streams -select_streams a `"$filePath`" 2>&1
            if (-not $ffprobeOutput) {
                Write-Verbose "文件无音频流: $filePath"
                $hasAudio = $false
            }

            if ($hasAudio) {
                Write-Verbose "分析音频: $filePath"
                
                # 使用 ffmpeg 的 loudnorm 滤镜获取音频统计信息（使用 Out-String 修复输出问题）
                $ffmpegOutput = ffmpeg -nostats -i `"$filePath`" -filter_complex loudnorm=print_format=json -f null - 2>&1 | Out-String
                
                # 从输出中提取 JSON 数据
                $jsonStart = $ffmpegOutput.IndexOf('{')
                $jsonEnd = $ffmpegOutput.LastIndexOf('}') + 1
                if ($jsonStart -ge 0 -and $jsonEnd -gt $jsonStart) {
                    $jsonString = $ffmpegOutput.Substring($jsonStart, $jsonEnd - $jsonStart)
                    
                    try {
                        $stats = $jsonString | ConvertFrom-Json
                        $input_i = [float]($stats.input_i)
                        $input_tp = [float]($stats.input_tp)
                        
                        # 存储分析结果
                        $analysisData += [PSCustomObject]@{
                            FilePath = $filePath
                            LUFS     = $input_i
                            TP       = $input_tp
                            Diff     = $input_tp - $input_i
                            IsVideo  = $videoExtensions -contains $extension
                        }
                        Write-Verbose "  分析结果: LUFS=$("{0:F2}" -f $input_i) dB | TP=$("{0:F2}" -f $input_tp) dB"
                    }
                    catch {
                        Write-Warning "  错误: 无法解析分析数据，跳过此文件: $filePath"
                        Write-Debug "JSON 解析错误: $jsonString"
                    }
                }
                else {
                    Write-Warning "  错误: 未找到分析数据，跳过此文件: $filePath"
                    Write-Debug "FFmpeg 输出: $ffmpegOutput"
                }
            }
        }

        # 检查是否找到有效文件
        if ($analysisData.Count -eq 0) {
            Write-Verbose "未找到任何包含音频的有效文件！"
            return
        }

        # 第二步：计算全局增益参数
        $maxDiff = ($analysisData | Measure-Object -Property Diff -Maximum).Maximum
        $targetLUFS = $TargetPeak - $maxDiff
        
        Write-Verbose "`n计算结果:"
        Write-Verbose "  最大差值 (TP - LUFS) = $("{0:F2}" -f $maxDiff) dB"
        Write-Verbose "  目标 LUFS = $("{0:F2}" -f $targetLUFS) dB"

        # 第二步：计算全局增益参数
        $maxDiff = ($analysisData | Measure-Object -Property Diff -Maximum).Maximum
        $targetLUFS = $TargetPeak - $maxDiff
        
        Write-Verbose "`n计算结果:"
        Write-Verbose "  最大差值 (TP - LUFS) = $("{0:F2}" -f $maxDiff) dB"
        Write-Verbose "  目标 LUFS = $("{0:F2}" -f $targetLUFS) dB"

        # 第三步：处理所有文件
        Write-Host "目标音量: $("{0:F2}" -f $TargetPeak) dB" -ForegroundColor cyan

        $totalFiles = $analysisData.Count
        for ($i = 0; $i -lt $totalFiles; $i++) {
            $fileData = $analysisData[$i]
            Write-Progress -Activity "处理文件" -Status "正在处理: $($fileData.FilePath)" -PercentComplete (($i / $totalFiles) * 100)

            $inFile = $fileData.FilePath
            $extension = [System.IO.Path]::GetExtension($inFile).ToLower()
            $isVideo = $fileData.IsVideo
            $isAudio = -not $isVideo
            
            # 计算增益值
            $gain = $targetLUFS - $fileData.LUFS
            
            # 格式化浮点数保留2位小数
            $formattedLUFS = "{0:F2}" -f $fileData.LUFS
            $formattedTP = "{0:F2}" -f $fileData.TP
            $formattedGain = "{0:F2}" -f $gain
            
            # 设置输出文件
            if ($isAudio) {
                $outFile = [System.IO.Path]::ChangeExtension($inFile, ".mp3")
                $tempFile = [System.IO.Path]::ChangeExtension($inFile, ".temp.mp3")
            }
            else {
                $outFile = [System.IO.Path]::ChangeExtension($inFile, ".mp4")
                $tempFile = [System.IO.Path]::ChangeExtension($inFile, ".temp.mp4")
            }
            
            # 构建输出信息
            $fileInfo = [PSCustomObject]@{
                File         = $inFile
                Type         = if ($isVideo) { "Video" } else { "Audio" }
                OriginalLUFS = $formattedLUFS
                TP           = $formattedTP
                TargetLUFS   = "{0:F2}" -f $targetLUFS
                Gain         = $formattedGain
                Status       = "Pending"
            }
            
            Write-Verbose "`n文件: $inFile"
            Write-Verbose "  类型: $(if ($isVideo) { '视频' } else { '音频' })"
            Write-Verbose "  原始LUFS: $formattedLUFS dB"
            Write-Verbose "  原始TP: $formattedTP dB"
            Write-Verbose "  目标增益: $formattedGain dB"
            
            # 使用 ShouldProcess 确认操作
            $operation = "转换文件: $inFile -> $outFile，增益: $formattedGain dB"
            if ($PSCmdlet.ShouldProcess($operation, $inFile, "调整文件音量增益 $formattedGain dB")) {
                try {
                    # 构建 FFmpeg 命令
                    $ffmpegCmd = "ffmpeg -i `"$inFile`" "
                    
                    # 视频处理参数
                    if ($isVideo) {
                        $ffmpegCmd += "-c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p "
                        
                        # 音频处理
                        $ffmpegCmd += "-af `"volume=$($gain)dB`" "
                        $ffmpegCmd += "-c:a aac -b:a 320k "
                        
                        $ffmpegCmd += "-c:s mov_text -map_metadata 0 "
                    }
                    # 音频处理参数
                    else {
                        $ffmpegCmd += "-af `"volume=$($gain)dB`" "
                        $ffmpegCmd += "-c:a libmp3lame -b:a 320k "
                    }
                    
                    $ffmpegCmd += "-y `"$tempFile`""
                    
                    # 执行转换
                    Write-Verbose "  执行命令: $ffmpegCmd"
                    $ffmpegOutput = Invoke-Expression $ffmpegCmd 2>&1 | Out-String
                    
                    # 处理输出文件
                    if (Test-Path $tempFile) {
                        Move-Item -Force $tempFile $outFile
                        Write-Verbose "  已输出: $outFile"
                        
                        # 删除原始文件（如果是非目标格式）
                        $shouldDelete = ($isAudio -and $extension -ne '.mp3') -or ($isVideo -and $extension -ne '.mp4')
                        if ($shouldDelete -and ($inFile -ne $outFile)) {
                            if ($PSCmdlet.ShouldProcess("删除原始文件: $inFile", "确认删除?", "文件删除")) {
                                Remove-Item $inFile -Force
                                Write-Verbose "  已删除原始文件"
                            }
                        }
                        $fileInfo.Status = "Processed"
                    }
                    else {
                        $fileInfo.Status = "Failed"
                        Write-Warning "  错误: 转换失败，跳过此文件"
                        Write-Debug "FFmpeg 输出: $ffmpegOutput"
                    }
                }
                catch {
                    $fileInfo.Status = "Error"
                    Write-Error "  处理失败: $inFile"
                    Write-Error "  错误详情: $_"
                }
            }
            else {
                $fileInfo.Status = "Skipped"
                Write-Verbose "  用户取消处理: $inFile"
            }
            
            $results += $fileInfo
        }
    }

    end {
        # 输出总结信息
        $processedCount = ($results | Where-Object { $_.Status -eq "Processed" }).Count
        $skippedCount = ($results | Where-Object { $_.Status -eq "Skipped" }).Count
        $failedCount = ($results | Where-Object { $_.Status -in @("Failed", "Error") }).Count
        
        Write-Host "`n操作完成! "
        Write-Host "  成功处理: $processedCount 个文件"
        Write-Host "  跳过处理: $skippedCount 个文件"
        Write-Host "  处理失败: $failedCount 个文件"
        
        # 返回处理结果
        return $results
    }
}